package org.jslaughter.component.action;


import java.util.ArrayList;
import java.util.Collection;

import org.jslaughter.event.eventtype.DeathEvent;
import org.jslaughter.event.eventtype.DeathListener;
import org.nvframe.component.AbstractComponent;
import org.nvframe.component.state.Position;
import org.nvframe.entity.Entity;
import org.nvframe.event.EventService;
import org.nvframe.event.eventtype.UpdateEvent;
import org.nvframe.event.eventtype.UpdateListener;
import org.nvframe.exception.NVFrameException;
import org.nvframe.factory.EntityFactory;
import org.nvframe.util.Settings;

/**
 * 
 * @author Nik Van Looy
 */
public class SpawnAction extends AbstractComponent implements DeathListener, UpdateListener {

	private final int minSpawnDelayMs;
	private final int maxSpawnDelayMs;
	private final int maxSimultaneousSpawns;
	private final int maxActiveSpawns;
	private final int offset;
	private final String entityId;
	private final boolean copyPosition;

	private float spawnTimer;
	private float nextSpawnDelayMs;
	private Collection<Entity> activeSpawns;
	
	public SpawnAction(String id, Entity owner, Settings settings) throws NVFrameException {
		super(id, owner);
		
		if(!settings.containsKey("entityId"))
			throw new NVFrameException("cannot spawn entity withouth an entityId");
		
		entityId = settings.getString("entityId");
		
		minSpawnDelayMs = settings.getInt("minSpawnDelayMs", 1000);
		maxSpawnDelayMs = settings.getInt("maxSpawnDelayMs", 4000);
		maxSimultaneousSpawns = settings.getInt("maxSimultaneousSpawns", 1);
		maxActiveSpawns = settings.getInt("maxActiveSpawns", 1);
		copyPosition = settings.getBoolean("copyPosition", false);
		offset = settings.getInt("offset", 20);
				
		activeSpawns = new ArrayList<Entity>();
				
		spawnTimer = 0;
		nextSpawnDelayMs = calculateSpawnDelay();
		
		EventService.getInstance().addEventListener(this);
	}
	
	public float calculateSpawnDelay() {
		return (float) ((Math.random() * (maxSpawnDelayMs - minSpawnDelayMs)) + minSpawnDelayMs);
	}
	
	private void spawnEntity() throws NVFrameException {
		Entity entity = EntityFactory.getInstance().getEntityFromPrototype(entityId);
		Position position = (Position) entity.getComponent(Position.class);
		
		if(position == null)
			throw new NVFrameException("cannot set spawn position, entity: " + entityId);
		
		determineSpawnPosition(position);
		
		activeSpawns.add(entity);
	}

	private void determineSpawnPosition(Position position) {
		Position ownerPosition = (Position) owner.getComponent(Position.class);
		
		if(!copyPosition) {
			// random direction
			double direction = Math.toRadians((Math.random() * 359) + 360);
			
			position.setX(ownerPosition.getX() + (float) (Math.cos(direction) * offset));
			position.setY(ownerPosition.getY() + (float) (Math.sin(direction) * offset));
			position.setDirection((float) Math.toDegrees(direction));
		}
		else {
			position.setXY(ownerPosition.getXY());
			position.setDirection(ownerPosition.getDirection());
		}
	}
	
	@Override
	public void onUpdate(UpdateEvent event) {
		int delta = event.getDelta();
		
		if(activeSpawns.size() >= maxActiveSpawns) {
			spawnTimer = 0;
			return;
		}
		
		spawnTimer += delta;

		if (spawnTimer > nextSpawnDelayMs) {
			spawnTimer = 0;
			
			nextSpawnDelayMs = calculateSpawnDelay();
			
			// spawn the entity / entities
			try {
				for(int i = 0; i < maxSimultaneousSpawns; i++) {
					if(activeSpawns.size() < maxActiveSpawns) 
						spawnEntity();
				}
			} catch (NVFrameException e) {
				e.printStackTrace();
			}
		} 
	}
	
	@Override
	public void onDeath(DeathEvent event) {
		Entity entity = event.getEntity();
		
		if(activeSpawns.contains(entity))
			activeSpawns.remove(entity);
	}
	
	@Override
	public void removed() {
		EventService.getInstance().removeEventListener(this);
	}
}
